﻿#region

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

#endregion

namespace CuongHanh.DAL
{
    public class UnitOfWork : IDisposable
    {
        public MasterDbContext Context = new MasterDbContext();

        public void Save()
        {
            Context.SaveChanges();
        }

        #region Dispose Methods

        private bool _disposed;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if(!_disposed)
            {
                if(disposing)
                {
                    Context.Dispose();
                }
            }
            _disposed = true;
        }

        #endregion

        #region Repository Methods

        public DbSet<T> GetDBSet<T>() where T : class
        {
            return Context.Set<T>();
        }

        public virtual T FirstOrDefault<T>(
                Expression<Func<T, bool>> filter = null,
                Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                string includeProperties = "") where T : class
        {
            IQueryable<T> query = GetDBSet<T>();

            if(filter != null)
            {
                query = query.Where(filter);
            }

            foreach (string includeProperty in includeProperties.Split
                    (new[] {','},
                     StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            return orderBy != null
                           ? orderBy(query).FirstOrDefault()
                           : query.FirstOrDefault();
        }

        [Obsolete("Use method Where<T> instead.")]
        public virtual IQueryable<T> Select<T>(
                Expression<Func<T, bool>> filter = null,
                Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                string includeProperties = "") where T : class
        {
            IQueryable<T> query = GetDBSet<T>();

            if(filter != null)
            {
                query = query.Where(filter);
            }

            foreach (string includeProperty in includeProperties.Split
                    (new[] {','},
                     StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            return orderBy != null
                           ? orderBy(query)
                           : query;
        }

        public virtual IQueryable<T> Where<T>(
                Expression<Func<T, bool>> filter = null,
                Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                string includeProperties = "") where T : class
        {
            IQueryable<T> query = GetDBSet<T>();

            if(filter != null)
            {
                query = query.Where(filter);
            }

            foreach (string includeProperty in includeProperties.Split
                    (new[] {','},
                     StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            return orderBy != null
                           ? orderBy(query)
                           : query;
        }

        [Obsolete("This method is replaced by WhereToList<T>()")]
        public virtual List<T> SelectToList<T>(
                Expression<Func<T, bool>> filter = null,
                Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                string includeProperties = "") where T : class
        {
            IQueryable<T> query = GetDBSet<T>();

            if(filter != null)
            {
                query = query.Where(filter);
            }

            foreach (string includeProperty in includeProperties.Split
                    (new[] {','},
                     StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            return orderBy != null
                           ? orderBy(query).ToList()
                           : query.ToList();
        }

        public virtual List<T> WhereToList<T>(
                Expression<Func<T, bool>> filter = null,
                Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                string includeProperties = "") where T : class
        {
            return Where(filter,
                         orderBy,
                         includeProperties).ToList();
        }

        public virtual T GetById<T>(object id) where T : class
        {
            return GetDBSet<T>().Find(id);
        }

        public virtual void Insert<T>(T entity) where T : class
        {
            GetDBSet<T>().Add(entity);
        }

        public virtual void Delete<T>(object id) where T : class
        {
            T entityToDelete = GetDBSet<T>().Find(id);
            Delete(entityToDelete);
        }

        public virtual void Delete<T>(T entityToDelete) where T : class
        {
            if(Context.Entry(entityToDelete).State
               == EntityState.Detached)
            {
                GetDBSet<T>().Attach(entityToDelete);
            }
            GetDBSet<T>().Remove(entityToDelete);
        }

        public virtual void Update<T>(T entityToUpdate) where T : class
        {
            GetDBSet<T>().Attach(entityToUpdate);
            Context.Entry(entityToUpdate).State = EntityState.Modified;
        }

        public virtual IEnumerable<T> GetWithRawSql<T>(string query,
                                                       params object[] parameters) where T : class
        {
            return GetDBSet<T>().SqlQuery(query,
                                          parameters).ToList();
        }

        #endregion
    }
}
